package com.erp.generic.utils;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.erp.hibernate.HibernateSessionFactory;


public class HibernateUtil {

	private static final SessionFactory sessionFactory = HibernateSessionFactory.getSessionFactory();
	private static final ThreadLocal<Session> sessionThreadLocal = new ThreadLocal<Session>();
	private static final ThreadLocal<Integer> sessionRefThreadLocal = new ThreadLocal<Integer>();
	private static final ThreadLocal<Transaction> transactionThreadLocal = new ThreadLocal<Transaction>();
	private static final ThreadLocal<Integer> transactionRefThreadLocal = new ThreadLocal<Integer>();


	private HibernateUtil()
	{
	}
	
	public static Session openSession() {
		Session session = sessionThreadLocal.get();
		if (session == null || !session.isOpen()) {
//		if (session == null) {
			session = sessionFactory.openSession();
			sessionThreadLocal.set(session);
			sessionRefThreadLocal.set(new Integer(1));
		} else {
			Integer nextRefCount = sessionRefThreadLocal.get();
			sessionRefThreadLocal.set(++nextRefCount);
		}
		return session;
	}
	
	public static void closeSession() {
		Session session = sessionThreadLocal.get();
		if (session != null) {
			Integer currentRefCount = sessionRefThreadLocal.get();
			if (--currentRefCount == 0) {
				session.close();
				sessionThreadLocal.set(null);
				sessionRefThreadLocal.set(null);
			} else {
				sessionRefThreadLocal.set(currentRefCount);
			}
		}
	}

	public static Transaction beginTransaction() {
		Session session = sessionThreadLocal.get();
		if (session == null)
			throw new RuntimeException("Null or Closed Session, Please use openSession()");
		
		Transaction transaction = transactionThreadLocal.get();
		if (transaction == null) {
			transaction = session.beginTransaction();
			transactionThreadLocal.set(transaction);
			transactionRefThreadLocal.set(new Integer(1));
		} else {
			Integer nextRefCount = transactionRefThreadLocal.get();
			transactionRefThreadLocal.set(++nextRefCount);
		}
		return transaction;
	}
	
	public static void commitTransaction() throws Exception {
		Transaction transaction = transactionThreadLocal.get();
		if (transaction == null)
			throw new RuntimeException("Null Transaction, Please use beginTransaction()");

		if (transaction != null && !transaction.wasRolledBack() && !transaction.wasCommitted()) {
			Integer currentRefCount = transactionRefThreadLocal.get();
			if (--currentRefCount == 0) {
				try {
					transaction.commit();
				} 
				catch (Exception e) {
					rollbackTransaction();
					throw e;
				}
				finally {
					transactionThreadLocal.set(null);
					transactionRefThreadLocal.set(null);
				}
			} else {
				transactionRefThreadLocal.set(currentRefCount);
			}
		}
	}
	
	public static void rollbackTransaction() {
		Transaction transaction = transactionThreadLocal.get();
		if (transaction == null)
			throw new RuntimeException("Null Transaction, Please use beginTransaction()");

		if (transaction != null && !transaction.wasRolledBack() && !transaction.wasCommitted()) {
			try {
				transaction.rollback();
			} finally {
				transactionThreadLocal.set(null);
				transactionRefThreadLocal.set(null);
			}
		}
		transaction = null;
	}
	
	public static String formatValue(Object value) {
		if (value == null)
			return "null";
		
		if (value instanceof Boolean) {
			if (((Boolean) value).booleanValue())
				return "'Y'";
			else
				return "'N'";
		} else if (value instanceof String) {
			String encodedString = value.toString();
			return "'" + encodedString + "'";
		}
		return value.toString();
	}

}
